package ypmits.sound {
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;	

	/**
	 * The SoundControl Class makes it possible to perform basic actions
	 * on a sound like 'play', 'pause', 'resume' and 'stop'. A SoundControl
	 * object used a SoundManager to manage its volume.
	 * 
	 * @author Hugo Dechesne
	 */
	public class SoundControl extends EventDispatcher implements ISoundControl
	{
		private var _sound:Sound;
		private var _channel:SoundChannel;
		private var _pausePosition:Number = -1;
		private var _loops:int;
		private var _manager:SoundManager;
		private var _volume : Number = 1;
		
		/**
		 * Create a new instance of SoundControl. 
		 * 
		 * @param sound 
		 * @param volume
		 * 
		 */
		function SoundControl(sound:Sound, manager:SoundManager) {
			_sound = sound;
			_manager = manager;	
		}
		
		/**
		 * 
		 * @return The SoundAsset used to playback the sound
		 * 
		 */
		public function get sound():Sound {
			return _sound;
		}			
		
		/**
		 * Start playing the sound at a specific position and for a number of times
		 * 
		 * @param startTime The time at which the sound starts playing
		 * @param loops The number of times the sound plays
		 * 
		 */
		public function play(startTime:Number=0, loops:int=0):void {
			// check is there a sound object set
			if(!_sound) {
				return;
			}
			
			// if sound is already playing, stop it first
			if(_channel) {
				stop();
			}
			
			// set pause position to -1
			_pausePosition = -1;
			
			// play sound and set sound channel
			_channel = _sound.play(startTime, 0, new SoundTransform(_manager.volume * _volume));
			
			// return if the channel is null (user might not have soundcard)
			if(_channel == null) {
				return;
			}
			
			// set number of loops
			_loops = loops;
			
			// listen to sound complete for looping the sound
			_channel.addEventListener(Event.SOUND_COMPLETE, onSoundEvent);
			
			// listen to sound volume update
			_manager.addEventListener(Event.CHANGE, onSoundUpdate, false, 0, true);
			
			// listen to sound volume update
			_manager.addEventListener(Event.UNLOAD, onSoundUnload, false, 0, true);		
		}
		
		/**
		 * Stops to play the sound
		 * 
		 */
		public function stop():void {	
			if(_channel) {
				_channel.stop();
				_channel = null;
			}
			
			// stop listening to sound volume update
			_manager.removeEventListener(Event.CHANGE, onSoundUpdate);		
		}
		
		/**
		 * Pauses the sound playback
		 * 
		 */
		public function pause():void {	
			if(_channel) {
				_pausePosition = _channel.position;
				stop();
			}			
		}
		
		/**
		 * Resumes to play the sound from the last position
		 * 
		 */
		public function resume():void {
			if(_pausePosition >= 0) {
				play(_pausePosition, _loops);				
			}
		}
		
		/**
		 * Sets the volume of this SoundControl, relative to the SoundManagers volume.
		 * For example: if the SoundManagers volume is 0.5 and this SoundControls volume
		 * is 0.5 as well. The actual volume will be 0.25;
		 */
		public function set volume( value : Number) : void {
			_volume = value;		
			 onSoundUpdate();
		}
		
		public function get volume() : Number {
			return _volume;
		}
		
		/**
		 * Returns the current position in milli seconds.
		 */
		public function get position() : Number {
			if(_channel) {
				return _channel.position;
			}
			return 0;
		}
		
		/**
		 * Returns the total length of the Sound object in milli seconds.
		 */
		public function get length() : Number {
			if(_sound) {
				return _sound.length;
			}
			return 0;
		}

		/**
		 * Invoked when the SoundAsset has finished playing the sound, and
		 * determines wheter the sound has to be played again.
		 * 
		 * @param e The Event
		 * 
		 */
		private function onSoundEvent(e:Event):void {
			switch(e.type) {
				case Event.SOUND_COMPLETE:
					if(_loops > 0) {
						play(0, --_loops);
					} else {
						// stop listening to sound volume update
						_manager.removeEventListener(Event.CHANGE, onSoundUpdate);
						dispatchEvent(e);
					}
				break;
			}
		}
		
		/**
		 * Invoked when the SoundManager dispatches a change event. Updates
		 * the volume.
		 */
		private function onSoundUpdate(e:Event = null):void {
			if(_channel) {
				_channel.soundTransform = new SoundTransform(_manager.volume * _volume);
			}
		}
		
		/**
		 * 
		 */
		private function onSoundUnload(e:Event = null):void {	
			try {		
				_sound.close();
			} catch( error:Error ) { };
			
			stop();
		}
	}
}